/** @format */

import { isEmpty, merge } from 'lodash'
import { recordErrorLog } from '../../utils/common/logger'
import { resultsModel } from '../../sdk'
import { CODE_TYPES } from '../../errorCode'
import { formatUpdatedDataByProjection, formatUpdateUserProjection } from '../../utils/common/format'
import { dbUpdate } from '../../utils/common/mongodb'
import { USER_DB_NAME } from '../../db'
import { cloundAlarm } from '../../utils/common/dingTalk'

// 前置更新
export default function preUpdate(checks: IFunction[]) {
  return function (target: Object, name: string, descriptor: PropertyDescriptor) {
    const method = descriptor.value

    descriptor.value = async function (...args: any[]) {
      let [context, baseInfos = {} as IControllerInfos, services = [], preCheckData = {}] = args
      let totalUpdateProjection: IPreUpdateQuery = {}
      for (let i = 0; i < checks.length; i++) {
        const checkFn = checks[i]

        let result: IPreUpdateQuery = await checkFn.apply(target, [context, { ...baseInfos }, services])
        result = result || {}
        // 校验报错
        if ((result as IErrorResult)?.success === false && (result as IErrorResult)?.code) {
          return result
        }

        totalUpdateProjection = merge({}, totalUpdateProjection, result)

        baseInfos = { ...baseInfos }
      }

      // 更新数据
      try {
        const updateResult = await preUpdateUser(context, baseInfos.userInfo, totalUpdateProjection)
        if (!updateResult) return resultsModel.error(CODE_TYPES.SYSTEM_ERROR, '用户信息更新失败')
        // 避免再次查询
        baseInfos.userInfo = formatUpdatedDataByProjection(baseInfos.userInfo, totalUpdateProjection)
      } catch (error) {
        console.log(error, 'preUpdateUser-error')
        recordErrorLog(context, baseInfos, error.toString(), error.stack)
        // 云告警
        cloundAlarm(context, error.toString())
        return resultsModel.error(CODE_TYPES.SYSTEM_ERROR)
      }
      return method.apply(target, [context, { ...baseInfos }, services, preCheckData])
    }
  }
}

async function preUpdateUser(context: IContext<IParams>, userInfo: IUserInfo, updateProjection: IPreUpdateQuery) {
  // 删除空的操作
  if (isEmpty(updateProjection?.$inc)) {
    delete updateProjection.$inc
  }
  if (isEmpty(updateProjection?.$set)) {
    delete updateProjection.$set
  }
  if (isEmpty(updateProjection?.$push)) {
    delete updateProjection.$push
  }
  if (isEmpty(updateProjection?.$where)) {
    delete updateProjection.$where
  }
  if (isEmpty(updateProjection)) return true

  const query = updateProjection?.$where
    ? {
        _id: userInfo?._id,
        $where: updateProjection?.$where
      }
    : {
        _id: userInfo?._id
      }

  if (updateProjection?.$where) {
    delete updateProjection?.$where
  }

  return await dbUpdate(context, USER_DB_NAME, query, formatUpdateUserProjection(updateProjection))
}
